package com.yiruantong.common.core.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * stream 流工具类
 *
 * @author YiRuanTong
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StreamUtils {

  /**
   * 将collection过滤
   *
   * @param collection 需要转化的集合
   * @param function   过滤方法
   * @return 过滤后的list
   */
  public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
    if (CollUtil.isEmpty(collection)) {
      return CollUtil.newArrayList();
    }
    // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
    return collection.stream().filter(function).collect(Collectors.toList());
  }

  /**
   * 找到流中满足条件的第一个元素
   *
   * @param collection 需要查询的集合
   * @param function   过滤方法
   * @return 找到符合条件的第一个元素，没有则返回null
   */
  public static <E> E findFirst(Collection<E> collection, Predicate<E> function) {
    if (CollUtil.isEmpty(collection)) {
      return null;
    }
    return collection.stream().filter(function).findFirst().orElse(null);
  }

  /**
   * 找到流中任意一个满足条件的元素
   *
   * @param collection 需要查询的集合
   * @param function   过滤方法
   * @return 找到符合条件的任意一个元素，没有则返回null
   */
  public static <E> Optional<E> findAny(Collection<E> collection, Predicate<E> function) {
    if (CollUtil.isEmpty(collection)) {
      return Optional.empty();
    }
    return collection.stream().filter(function).findAny();
  }

  /**
   * 将collection拼接
   *
   * @param collection 需要转化的集合
   * @param function   拼接方法
   * @return 拼接后的list
   */
  public static <E> String join(Collection<E> collection, Function<E, String> function) {
    return join(collection, function, StringUtils.SEPARATOR_COMMA);
  }

  /**
   * 将collection拼接
   *
   * @param collection 需要转化的集合
   * @param function   拼接方法
   * @param delimiter  拼接符
   * @return 拼接后的list
   */
  public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
    if (CollUtil.isEmpty(collection)) {
      return StringUtils.EMPTY;
    }
    return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
  }

  /**
   * 将collection排序
   *
   * @param collection 需要转化的集合
   * @param comparing  排序方法
   * @return 排序后的list
   */
  public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
    if (CollUtil.isEmpty(collection)) {
      return CollUtil.newArrayList();
    }
    // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
    return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
  }

  /**
   * 将collection转化为类型不变的map<br>
   * <B>{@code Collection<V>  ---->  Map<K,V>}</B>
   *
   * @param collection 需要转化的集合
   * @param key        V类型转化为K类型的lambda方法
   * @param <V>        collection中的泛型
   * @param <K>        map中的key类型
   * @return 转化后的map
   */
  public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
    if (CollUtil.isEmpty(collection)) {
      return MapUtil.newHashMap();
    }
    return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
  }

  /**
   * 将Collection转化为map(value类型与collection的泛型不同)<br>
   * <B>{@code Collection<E> -----> Map<K,V>  }</B>
   *
   * @param collection 需要转化的集合
   * @param key        E类型转化为K类型的lambda方法
   * @param value      E类型转化为V类型的lambda方法
   * @param <E>        collection中的泛型
   * @param <K>        map中的key类型
   * @param <V>        map中的value类型
   * @return 转化后的map
   */
  public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
    if (CollUtil.isEmpty(collection)) {
      return MapUtil.newHashMap();
    }
    return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
  }

  /**
   * 将collection按照规则(比如有相同的班级id)分类成map<br>
   * <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
   *
   * @param collection 需要分类的集合
   * @param key        分类的规则
   * @param <E>        collection中的泛型
   * @param <K>        map中的key类型
   * @return 分类后的map
   */
  public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
    if (CollUtil.isEmpty(collection)) {
      return MapUtil.newHashMap();
    }
    return collection
      .stream().filter(Objects::nonNull)
      .collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
  }

  /**
   * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
   * <B>{@code Collection<E>  --->  Map<T,Map<U,List<E>>> } </B>
   *
   * @param collection 需要分类的集合
   * @param key1       第一个分类的规则
   * @param key2       第二个分类的规则
   * @param <E>        集合元素类型
   * @param <K>        第一个map中的key类型
   * @param <U>        第二个map中的key类型
   * @return 分类后的map
   */
  public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
    if (CollUtil.isEmpty(collection)) {
      return MapUtil.newHashMap();
    }
    return collection
      .stream().filter(Objects::nonNull)
      .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
  }

  /**
   * 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
   * <B>{@code Collection<E>  --->  Map<T,Map<U,E>> } </B>
   *
   * @param collection 需要分类的集合
   * @param key1       第一个分类的规则
   * @param key2       第二个分类的规则
   * @param <T>        第一个map中的key类型
   * @param <U>        第二个map中的key类型
   * @param <E>        collection中的泛型
   * @return 分类后的map
   */
  public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
    if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
      return MapUtil.newHashMap();
    }
    return collection
      .stream().filter(Objects::nonNull)
      .collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
  }

  /**
   * 将collection转化为List集合，但是两者的泛型不同<br>
   * <B>{@code Collection<E>  ------>  List<T> } </B>
   *
   * @param collection 需要转化的集合
   * @param function   collection中的泛型转化为list泛型的lambda表达式
   * @param <E>        collection中的泛型
   * @param <T>        List中的泛型
   * @return 转化后的list
   */
  public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
    if (CollUtil.isEmpty(collection)) {
      return CollUtil.newArrayList();
    }
    return collection
      .stream()
      .map(function)
      .filter(Objects::nonNull)
      // 注意此处不要使用 .toList() 新语法 因为返回的是不可变List 会导致序列化问题
      .collect(Collectors.toList());
  }

  /**
   * 将collection转化为List<Map<String, Object>> 集合<br>
   * <B>{@code Object  ------>  List<Map<String, Object>> } </B>
   *
   * @param value 需要转化的集合
   * @param <E>   collection中的泛型
   * @return 转化后的list
   */
  public static <E> List<Map<String, Object>> toList(Object value) {
    if (value instanceof ArrayList<?>) {
      List<?> collection = Convert.toList(value);
      return Convert.toList(Map.class, collection).stream().map(BeanUtil::beanToMap).toList();
    } else {
      return new ArrayList<>();
    }
  }

  /**
   * 将collection转化为Set集合，但是两者的泛型不同<br>
   * <B>{@code Collection<E>  ------>  Set<T> } </B>
   *
   * @param collection 需要转化的集合
   * @param function   collection中的泛型转化为set泛型的lambda表达式
   * @param <E>        collection中的泛型
   * @param <T>        Set中的泛型
   * @return 转化后的Set
   */
  public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
    if (CollUtil.isEmpty(collection) || function == null) {
      return CollUtil.newHashSet();
    }
    return collection
      .stream()
      .map(function)
      .filter(Objects::nonNull)
      .collect(Collectors.toSet());
  }


  /**
   * 合并两个相同key类型的map
   *
   * @param map1  第一个需要合并的 map
   * @param map2  第二个需要合并的 map
   * @param merge 合并的lambda，将key  value1 value2合并成最终的类型,注意value可能为空的情况
   * @param <K>   map中的key类型
   * @param <X>   第一个 map的value类型
   * @param <Y>   第二个 map的value类型
   * @param <V>   最终map的value类型
   * @return 合并后的map
   */
  public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
    if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
      return MapUtil.newHashMap();
    } else if (MapUtil.isEmpty(map1)) {
      map1 = MapUtil.newHashMap();
    } else if (MapUtil.isEmpty(map2)) {
      map2 = MapUtil.newHashMap();
    }
    Set<K> key = new HashSet<>();
    key.addAll(map1.keySet());
    key.addAll(map2.keySet());
    Map<K, V> map = new HashMap<>();
    for (K t : key) {
      X x = map1.get(t);
      Y y = map2.get(t);
      V z = merge.apply(x, y);
      if (z != null) {
        map.put(t, z);
      }
    }
    return map;
  }

  /**
   * 按某个指定字段去重
   *
   * @param keyExtractor 需要去重的字段
   * @return java.util.function.Predicate<T>
   * @author 陈賝
   * @date 2022/9/2 13:49
   */
  public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> {
      var k = keyExtractor.apply(t);
      if (k == null) k = "null";
      return seen.putIfAbsent(k, Boolean.TRUE) == null;
    };
  }

  /**
   * 对集合中某个字段进行求和汇总
   *
   * @param collection 数据集合
   * @param function   求和字段
   * @param <E>        集合对象泛型
   * @return 求和值
   */
  public static <E> BigDecimal sum(Collection<E> collection, Function<E, BigDecimal> function) {
    if (CollUtil.isEmpty(collection)) {
      return BigDecimal.ZERO;
    }
    return collection.stream()
      .filter(Objects::nonNull)  // 过滤掉 null 元素
      .map(function)
      .map(B::ifNullDefaultZero)  // 确保 BigDecimal 不是 null
      .reduce(BigDecimal.ZERO, BigDecimal::add);
  }
}
